"""
Exception classes for specification fixing.

Defines custom exceptions and error handling utilities
for the specification fixer module.
"""

from typing import Optional, Any, Dict


class SpecificationFixError(Exception):
    """Base exception for all specification fixing errors."""

    def __init__(self, message: str, error_code: Optional[str] = None,
                 details: Optional[Dict[str, Any]] = None):
        super().__init__(message)
        self.message = message
        self.error_code = error_code
        self.details = details or {}

    def to_dict(self) -> Dict[str, Any]:
        """Convert exception to dictionary for logging/serialization."""
        return {
            "error_type": self.__class__.__name__,
            "message": self.message,
            "error_code": self.error_code,
            "details": self.details
        }


class ValidationError(SpecificationFixError):
    """Raised when specification validation fails."""

    def __init__(self, message: str, field: Optional[str] = None,
                 value: Optional[Any] = None):
        super().__init__(message, "VALIDATION_ERROR")
        self.field = field
        self.value = value
        if field:
            self.details["field"] = field
        if value is not None:
            self.details["value"] = str(value)


class ParseError(SpecificationFixError):
    """Raised when specification parsing fails."""

    def __init__(self, message: str, file_path: Optional[str] = None,
                 line_number: Optional[int] = None):
        super().__init__(message, "PARSE_ERROR")
        self.file_path = file_path
        self.line_number = line_number
        if file_path:
            self.details["file_path"] = file_path
        if line_number:
            self.details["line_number"] = line_number


class StorageError(SpecificationFixError):
    """Raised when file storage operations fail."""

    def __init__(self, message: str, file_path: Optional[str] = None,
                 operation: Optional[str] = None):
        super().__init__(message, "STORAGE_ERROR")
        self.file_path = file_path
        self.operation = operation
        if file_path:
            self.details["file_path"] = file_path
        if operation:
            self.details["operation"] = operation


class ConfigurationError(SpecificationFixError):
    """Raised when configuration is invalid or missing."""

    def __init__(self, message: str, config_key: Optional[str] = None,
                 config_value: Optional[Any] = None):
        super().__init__(message, "CONFIGURATION_ERROR")
        self.config_key = config_key
        self.config_value = config_value
        if config_key:
            self.details["config_key"] = config_key
        if config_value is not None:
            self.details["config_value"] = str(config_value)


class AnalysisError(SpecificationFixError):
    """Raised when specification analysis fails."""

    def __init__(self, message: str, analysis_type: Optional[str] = None,
                 component: Optional[str] = None):
        super().__init__(message, "ANALYSIS_ERROR")
        self.analysis_type = analysis_type
        self.component = component
        if analysis_type:
            self.details["analysis_type"] = analysis_type
        if component:
            self.details["component"] = component


class ComponentError(SpecificationFixError):
    """Raised when component definition or validation fails."""

    def __init__(self, message: str, component_id: Optional[str] = None,
                 component_type: Optional[str] = None):
        super().__init__(message, "COMPONENT_ERROR")
        self.component_id = component_id
        self.component_type = component_type
        if component_id:
            self.details["component_id"] = component_id
        if component_type:
            self.details["component_type"] = component_type


class CriteriaError(SpecificationFixError):
    """Raised when acceptance criteria operations fail."""

    def __init__(self, message: str, criteria_id: Optional[str] = None,
                 metric_name: Optional[str] = None):
        super().__init__(message, "CRITERIA_ERROR")
        self.criteria_id = criteria_id
        self.metric_name = metric_name
        if criteria_id:
            self.details["criteria_id"] = criteria_id
        if metric_name:
            self.details["metric_name"] = metric_name


class TimeoutError(SpecificationFixError):
    """Raised when operations exceed time limits."""

    def __init__(self, message: str, timeout_seconds: Optional[float] = None,
                 operation: Optional[str] = None):
        super().__init__(message, "TIMEOUT_ERROR")
        self.timeout_seconds = timeout_seconds
        self.operation = operation
        if timeout_seconds:
            self.details["timeout_seconds"] = timeout_seconds
        if operation:
            self.details["operation"] = operation


def handle_exceptions(func):
    """Decorator for standardized exception handling."""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except SpecificationFixError:
            # Re-raise our custom exceptions
            raise
        except FileNotFoundError as e:
            raise StorageError(f"File not found: {e}", operation=str(func.__name__))
        except PermissionError as e:
            raise StorageError(f"Permission denied: {e}", operation=str(func.__name__))
        except (ValueError, TypeError) as e:
            raise ValidationError(f"Invalid data: {e}")
        except OSError as e:
            raise StorageError(f"Operating system error: {e}", operation=str(func.__name__))
        except Exception as e:
            raise SpecificationFixError(f"Unexpected error in {func.__name__}: {e}",
                                       "UNEXPECTED_ERROR",
                                       {"original_error": str(e)})
    return wrapper


def safe_execute(func, default_value=None, exception_type: type = SpecificationFixError):
    """Safely execute a function with exception handling."""
    try:
        return func()
    except Exception as e:
        if isinstance(e, exception_type):
            return default_value
        raise


def create_error_context(operation: str, **context) -> Dict[str, Any]:
    """Create error context dictionary for logging."""
    return {
        "operation": operation,
        "timestamp": str(__import__('datetime').datetime.now()),
        **context
    }